package com.ithinks.flow.common.utils.protocol.modbus.modbus4J;

import com.serotonin.modbus4j.BatchRead;
import com.serotonin.modbus4j.BatchResults;
import com.serotonin.modbus4j.ModbusFactory;
import com.serotonin.modbus4j.ModbusMaster;
import com.serotonin.modbus4j.code.DataType;
import com.serotonin.modbus4j.exception.ErrorResponseException;
import com.serotonin.modbus4j.exception.ModbusInitException;
import com.serotonin.modbus4j.exception.ModbusTransportException;
import com.serotonin.modbus4j.ip.IpParameters;
import com.serotonin.modbus4j.locator.BaseLocator;
import lombok.extern.slf4j.Slf4j;

/**
 * Serotonin Software用Java编写的Modbus协议的高性能且易于使用的实现。支持ASCII，RTU，TCP和UDP传输作为从站或主站，自动请求分区，响应数据类型解析和节点扫描。
 * Modbus设备可分为主站(poll)和从站(slave)。主站只有一个，从站有多个，主站向各从站发送请求帧，从站给予响应。在使用TCP通信时，主站为client端，主动建立连接；从站为server端，等待连接。
 *
 * modbus通讯，使用modbus4J实现
 */
@Slf4j
public class Modbus4JReaderUtils {

    static ModbusFactory modbusFactory;

    static {
        if(modbusFactory == null)
        {
            modbusFactory = new ModbusFactory();
        }
    }

    /**
     * 获取modbus master
     * @return
     */
    public static ModbusMaster getMaster(){

        IpParameters parameters = new IpParameters();

        parameters.setHost("localhost");

        parameters.setPort(502);

//        modbusFactory.createAsciiMaster();//ASCII协议
//        modbusFactory.createRtuMaster();//RTU协议
//        modbusFactory.createTcpMaster();//TCP协议
//        modbusFactory.createUdpMaster();//UDP协议
        ModbusMaster master = modbusFactory.createTcpMaster(parameters,false);
        try {
            master.init();
        } catch (ModbusInitException e) {
            e.printStackTrace();
            log.error("modbus master 初始化失败！{}",e.getMessage());
        }
        return master;
    }

    /**
     * 读取【01 coil status 0x】类型，开关数据
     * @return
     * @param offset 位置
     * @param slaveId slaveId
     */
    public static Boolean readCoilStatus(int slaveId, int offset){
        BaseLocator<Boolean> baseLocator = BaseLocator.coilStatus(slaveId, offset);
        Boolean value = null;
        try {
            value = getMaster().getValue(baseLocator);
        } catch (ModbusTransportException e) {
            e.printStackTrace();
            log.error("获取线圈状态，modbus master 通信异常！{}",e.getMessage());
        } catch (ErrorResponseException e) {
            e.printStackTrace();
            log.error("获取线圈状态modbus响应异常！{}",e.getMessage());
        } finally {
        }
        return value;
    }

    /**
     * 读取【02 input status 1x】类型，开关数据
     * @param slaveId
     * @param offset
     * @return
     */
    public static Boolean readInputStatus(int slaveId, int offset){
        BaseLocator<Boolean> locator = BaseLocator.inputStatus(slaveId,offset);
        Boolean value = false;
        try {
            value = getMaster().getValue(locator);
        } catch (ModbusTransportException e) {
            e.printStackTrace();
            log.error("获取输入状态：modbus master 通信异常！{}",e.getMessage());
        } catch (ErrorResponseException e) {
            log.error("获取输入状态：modbus响应异常！{}",e.getMessage());
            e.printStackTrace();
        } finally {
        }
        return value;
    }

    /**
     * 读取【03 holding register类型2x】模拟量数据
     * @param slaveId
     * @param offset
     * @param dataType
     * @return
     */
    public static Number readHoldingRegsiter(int slaveId,int offset,int dataType){
        BaseLocator<Number> locator = null;
        Number value = null;
        try {
            locator = BaseLocator.holdingRegister(slaveId,offset,dataType);
            value = getMaster().getValue(locator);
            return value;
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
        }
        return value;
    }
    /**
     * 读取[04 Input Registers 3x]类型 模拟量数据
     *
     * @param slaveId
     *            slaveId
     * @param offset
     *            位置
     * @param dataType
     *            数据类型,来自com.serotonin.modbus4j.code.DataType
     * @return 返回结果
     * @throws ModbusTransportException
     *             异常
     * @throws ErrorResponseException
     *             异常
     * @throws ModbusInitException
     *             异常
     */
    public static Number readInputRegisters(int slaveId, int offset, int dataType)
            throws ModbusTransportException, ErrorResponseException, ModbusInitException {
        // 04 Input Registers类型数据读取
        BaseLocator<Number> loc = BaseLocator.inputRegister(slaveId, offset, dataType);
        Number value = getMaster().getValue(loc);
        return value;
    }

    /**
     * 批量读取使用方法
     *
     * @throws ModbusTransportException
     * @throws ErrorResponseException
     * @throws ModbusInitException
     */
    public static void batchRead() throws ModbusTransportException, ErrorResponseException, ModbusInitException {

        BatchRead<Integer> batch = new BatchRead<Integer>();

        batch.addLocator(0, BaseLocator.holdingRegister(1, 1, DataType.FOUR_BYTE_FLOAT));
        batch.addLocator(1, BaseLocator.inputStatus(1, 0));

        ModbusMaster master = getMaster();

        batch.setContiguousRequests(false);
        BatchResults<Integer> results = master.send(batch);
        System.out.println(results.getValue(0));
        System.out.println(results.getValue(1));
    }


    /**
     * 测试
     *
     * @param args
     */
    public static void main(String[] args) {
        try {
            // 01测试
            Boolean v011 = readCoilStatus(1, 0);
            Boolean v012 = readCoilStatus(1, 1);
            Boolean v013 = readCoilStatus(1, 6);
            System.out.println("v011:" + v011);
            System.out.println("v012:" + v012);
            System.out.println("v013:" + v013);
            // 02测试
            Boolean v021 = readInputStatus(1, 0);
            Boolean v022 = readInputStatus(1, 1);
            Boolean v023 = readInputStatus(1, 2);
            System.out.println("v021:" + v021);
            System.out.println("v022:" + v022);
            System.out.println("v023:" + v023);

            // 03测试
            Number v031 = readHoldingRegsiter(1, 1, DataType.FOUR_BYTE_FLOAT);// 注意,float
            Number v032 = readHoldingRegsiter(1, 3, DataType.FOUR_BYTE_FLOAT);// 同上
            System.out.println("v031:" + v031);
            System.out.println("v032:" + v032);

            // 04测试
            Number v041 = readInputRegisters(1, 0, DataType.FOUR_BYTE_FLOAT);//
            Number v042 = readInputRegisters(1, 2, DataType.FOUR_BYTE_FLOAT);//
            System.out.println("v041:" + v041);
            System.out.println("v042:" + v042);
            // 批量读取
            batchRead();

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

}
